#include <D3Dcompiler.h>
#include "CBInterfaceDX10.h"
#include <d3d10shader.h>
#pragma comment (lib, "D3DCompiler.lib")

CCBInterfaceDX10::CCBInterfaceDX10( )
{
    ZeroMemory(m_pBuffersData, sizeof(m_pBuffersData));
    ZeroMemory(m_pBuffers, sizeof(m_pBuffers));
}

CCBInterfaceDX10::~CCBInterfaceDX10( )
{
    for (mUInt uBufInd = 0; uBufInd < ChangeFreqLast; uBufInd++)
    {
        delete [] m_pBuffersData[uBufInd].pData;
        SAFE_RELEASE(m_pBuffers[uBufInd]);
    }
}

mVoid CCBInterfaceDX10::InitBuffersData( ID3D10Blob *a_pShaderCode )
{
    ID3D10ShaderReflection *pShaderReflection;
    /* Initialization of constants in each buffer: calculating its offset in memory and name */
    HRESULT hr;
    /* must be D3DReflect, but microsoft sucks balls =| */
    hr = D3D10ReflectShader(a_pShaderCode->GetBufferPointer(), a_pShaderCode->GetBufferSize(),
        /*IID_ID3D10ShaderReflection, (void**)*/&pShaderReflection);
    //hr == S_OK ?
    /* 4 buffers - getting each by index and looking for name match */
    ID3D10ShaderReflectionConstantBuffer *pSRCB;
    D3D10_SHADER_BUFFER_DESC SRCBDesc;
    ID3D10ShaderReflectionVariable *pVarSRV;
    D3D10_SHADER_VARIABLE_DESC VarSVD;
    TConstantVariableData VarData;
    TCBufferData *pCurrentBufData;
    mUInt uVarOffset;
    for (mUInt uBufInd = 0; uBufInd < ChangeFreqLast; uBufInd++)
    {
        pSRCB = pShaderReflection->GetConstantBufferByIndex(uBufInd);
        pSRCB->GetDesc(&SRCBDesc);
        pCurrentBufData = m_pBuffersData;
        /* Looking for a match */
        for (mUInt uBufType = 0; uBufType < ChangeFreqLast; uBufType++)
        {
            if (g_sChangeFreqNames[uBufType] == SRCBDesc.Name)
                break;
            pCurrentBufData++;
        }
        pCurrentBufData->Size = SRCBDesc.Size;
        pCurrentBufData->pData = new mByte[SRCBDesc.Size];
        /* Since we have all info about buffer, 
        * let's calculating offset for each variable */
        uVarOffset = 0;
        for (mUInt uVarInd = 0; uVarInd < SRCBDesc.Variables; uVarInd++)
        {
            pVarSRV = pSRCB->GetVariableByIndex(uVarInd);
            pVarSRV->GetDesc(&VarSVD);
            VarData.Offset = uVarOffset;
            VarData.sName  = VarSVD.Name;
            pCurrentBufData->CVariables.push_back(VarData);
            uVarOffset += VarSVD.Size;
        }
    }
    SAFE_RELEASE(pShaderReflection);

    D3D10_BUFFER_DESC cbDesc;
    cbDesc.ByteWidth        = 0;
    cbDesc.Usage            = D3D10_USAGE_DYNAMIC;
    cbDesc.BindFlags        = D3D10_BIND_CONSTANT_BUFFER;
    cbDesc.CPUAccessFlags   = D3D10_CPU_ACCESS_WRITE;
    cbDesc.MiscFlags        = 0;

    /* Initializing cbuffers in vram */
    for (mUInt uBufInd = 0; uBufInd < ChangeFreqLast; uBufInd++)
    {
        cbDesc.ByteWidth = m_pBuffersData[uBufInd].Size;
        g_D3DDevice.pD3D10Device->CreateBuffer(&cbDesc, NULL, &m_pBuffers[uBufInd]);
    }
}

mBool CCBInterfaceDX10::SendConstantValue( const std::string &a_sConstName, mVoid *a_pValue, size_t a_ValueSize, TChangeFreq a_ChangeFreq )
{
    for (mUInt uVarInd = 0; uVarInd < m_pBuffersData[a_ChangeFreq].CVariables.size(); uVarInd++)
    {
        if (m_pBuffersData[a_ChangeFreq].CVariables[uVarInd].sName == a_sConstName)
        {
            mByte *pDst = (mByte*)m_pBuffersData[a_ChangeFreq].pData;
            mUInt uOffs = m_pBuffersData[a_ChangeFreq].CVariables[uVarInd].Offset;
            memcpy_s(pDst + uOffs, a_ValueSize, a_pValue, a_ValueSize);
            return true;
        }
    }
    //error! variable not found 
    return false;
}

mVoid CCBInterfaceDX10::UpdateConstantValues( TChangeFreq a_ChangeFreq )
{
    mVoid *pMappedBuf;
    m_pBuffers[a_ChangeFreq]->Map( D3D10_MAP_WRITE_DISCARD, NULL, &pMappedBuf );
    size_t Size = m_pBuffersData[a_ChangeFreq].Size;
    mVoid *pSrc = m_pBuffersData[a_ChangeFreq].pData;
    memcpy_s(pMappedBuf, Size, pSrc, Size);
    m_pBuffers[a_ChangeFreq]->Unmap();
}